package com.cuijq.ms.observer.v7;

import com.google.common.base.Preconditions;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @author by CuiJQ
 * @Classname ObserverRegistry
 * @Description ObserverRegistry Description
 * @Date 2021/5/28 16:20
 * 当调用 register() 函数注册观察者的时候，EventBus 通过解析 @Subscribe 注解，
 * 生成 Observer 注册表。当调用 post() 函数发送消息的时候，EventBus 通过注册表找到相应的可接收消息的函数，
 * 然后通过 Java 的反射语法来动态地创建对象、执行函数。对于同步阻塞模式，
 * EventBus 在一个线程内依次执行相应的函数。对于异步非阻塞模式，EventBus 通过一个线程池来执行相应的函数。
 */
public class ObserverRegistry {

  private ConcurrentMap<Class<?>, CopyOnWriteArraySet> registry = new ConcurrentHashMap<>();

  //CopyOnWriteArraySet，顾名思义，在写入数据的时候，
  // 会创建一个新的 set，并且将原始数据 clone 到新的 set 中，
  // 在新的 set 中写入数据完成之后，再用新的 set 替换老的 set

  /**
   * 调用register
   * @param observer
   */
  public void register(Object observer) {
    Map<Class<?>, Collection<ObserverAction>> observerActions = findAllObserverActions(observer);
    for (Map.Entry<Class<?>, Collection<ObserverAction>> entry : observerActions.entrySet()) {
      Class eventType = entry.getKey();
      Collection eventActions = entry.getValue();
      CopyOnWriteArraySet registeredEventActions = registry.get(eventType);
      if (registeredEventActions == null) {
        registry.putIfAbsent(eventType, new CopyOnWriteArraySet<>());
        registeredEventActions = registry.get(eventType);
      }
      registeredEventActions.addAll(eventActions);
    }
  }

  /**
   * 根据事件，查询所有的观察者
   * @param event
   * @return
   */
  public List<ObserverAction> getMatchedObserverActions(Object event) {
    List<ObserverAction> matchedObservers = new ArrayList<>();
    Class<?> postedEventType = event.getClass();
    for (Map.Entry<Class<?>, CopyOnWriteArraySet> entry : registry.entrySet()) {
      Class eventType = entry.getKey();
      Collection eventActions = entry.getValue();
      //eventType 是不是  postedEventType 的子类或者子接口
      if (postedEventType.isAssignableFrom(eventType)) {
        matchedObservers.addAll(eventActions);
      }
    }
    return matchedObservers;
  }

  /**
   *  按照observer  查询所有的观察者
   * @param observer
   * @return
   */
  private Map<Class<?>, Collection<ObserverAction>> findAllObserverActions(Object observer) {
    Map<Class<?>, Collection<ObserverAction>> observerActions = new HashMap<>();
    Class clazz = observer.getClass();
    for (Method method : getAnnotatedMethods(clazz)) {
      Class[] parameterTypes = method.getParameterTypes();
      Class eventType = parameterTypes[0];
      if (!observerActions.containsKey(eventType)) {
        observerActions.put(eventType, new ArrayList<>());
      }
      observerActions.get(eventType).add(new ObserverAction(observer, method));
    }
    return observerActions;
  }

  /**
   * 获取clazz类中 有Subscribe注解的方法
   * @param clazz
   * @return
   */
  private List<Method> getAnnotatedMethods(Class<?> clazz) {
    List<Method> annotatedMethods = new ArrayList<>();
    for (Method method : clazz.getDeclaredMethods()) {
      /**
       * 注释 Subscribe 是否在 method 方法上
       */
      if (method.isAnnotationPresent(Subscribe.class)) {
        /**
         * 获取此方法的形式参数类型
         */
        Class[] parameterTypes = method.getParameterTypes();
        Preconditions.checkArgument(parameterTypes.length == 1,
            "Method %s has @Subscribe annotation but has %s parameters."
                + "Subscriber methods must have exactly 1 parameter.", method, parameterTypes.length);
        annotatedMethods.add(method);
      }
    }
    return annotatedMethods;
  }
}
